#include <ani.h>
#include <array>
#include <iostream>
#include <vector>


std::string ANIUtils_ANIStringToStdString(ani_env *env,ani_string ani_str){
    ani_size  strSize;
    env->String_GetUTF8Size(ani_str, &strSize);
   
    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char* utf8_buffer = buffer.data();

    //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written);
    
    utf8_buffer[bytes_written] = '\0'; 
    std::string content = std::string(utf8_buffer); 
    return content;
}

static ani_enum_item getBlue([[maybe_unused]] ani_env *env)
{
    ani_enum enumType;
    env->FindEnum("ani_enum.COLORINT", &enumType);

    ani_enum_item enumItem;
    env->Enum_GetEnumItemByIndex(enumType, ani_size(2), &enumItem);
    // env->Enum_GetEnumItemByName(enumType, "BLUEINT", &enumItem); // 与上面的方法等效
    return enumItem;
}

static ani_enum_item processEnumInt([[maybe_unused]] ani_env *env, ani_enum_item enumObj)
{
    ani_string inputEnumName;
    // 通过enum实例获取enum属性的ani_string
    env->EnumItem_GetName(enumObj, &inputEnumName);

    auto inputEnumNameStr = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(inputEnumName) );
    std::cout << "inputEnumName :" << inputEnumNameStr << std::endl;

    ani_enum enumType;
    if(ANI_OK != env->FindEnum("ani_enum.COLORINT", &enumType)) {
        std::cerr << "Find Enum Faild" << std::endl;
    }

    ani_enum_item enumItem;
    // 通过enum属性的字符串获取enum实例
    env->Enum_GetEnumItemByName(enumType, inputEnumNameStr.c_str(), &enumItem);
    return enumItem;
    
}
  
static ani_enum_item processEnumIntReturnNext([[maybe_unused]] ani_env *env, ani_enum_item enumObj)
{
    ani_size enumIndex;
    // 通过enum的实例获取其属性在enum中对应的序号
    env->EnumItem_GetIndex(enumObj, &enumIndex);
    std::cout << "inputEnumIndex :" << enumIndex << std::endl;

    ani_int enumValue;
    // 通过enum的实例获取其属性的值
    env->EnumItem_GetValue_Int(enumObj, &enumValue);
    std::cout << "inputEnumValue :" << enumValue << std::endl;

    ani_enum enumType;
    if(ANI_OK != env->FindEnum("ani_enum.COLORINT", &enumType)) {
        std::cerr << "Find Enum Faild" << std::endl;
    }

    ani_enum_item enumItem;
    // Enum_GetEnumItemByIndex和 Enum_GetEnumItemByName相当于Enum的Object_New
    env->Enum_GetEnumItemByIndex(enumType, enumIndex + 1, &enumItem);

    ani_string nextEnumName;
    env->EnumItem_GetName(enumItem, &nextEnumName);
    auto nextEnumNameStr = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(nextEnumName) );
    std::cout << "nextEnumName :" << nextEnumNameStr << std::endl;

    return enumItem;
    
}


static ani_object getCOLORSTRING([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object, ani_int enumIndex)
{
    ani_enum enumType;
    env->FindEnum("ani_enum.COLORSTRING", &enumType);

    ani_enum_item enumItem;
    // Enum_GetEnumItemByIndex和 Enum_GetEnumItemByName相当于Enum的Object_New
    env->Enum_GetEnumItemByIndex(enumType, enumIndex, &enumItem);

    {
        ani_enum enumType;
        // 这个方法可以从enumItem获取到enumType
        env->EnumItem_GetEnum(enumItem, &enumType);
    }

    ani_string stringValue;
    env->EnumItem_GetValue_String(enumItem, &stringValue);

    auto str = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(stringValue));
    std::cout << "Enum Value : " << str << std::endl;

    return enumItem;
}

static ani_int returnEnumInt([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object) {
    ani_enum enumType;
    if(ANI_OK != env->FindEnum("ani_enum.#COLORINT", &enumType)) {
        std::cerr << "Find Enum Faild" << std::endl;
    }
    
    ani_enum_item enumItem;
    if(ANI_OK != env->Enum_GetEnumItemByName(enumType, "GREENINT", &enumItem)){
        std::cerr << "Enum_GetEnumItemByName FAILD" << std::endl;
    }

    ani_size idx;
    if(ANI_OK != env->EnumItem_GetIndex(enumItem, &idx)){
        std::cerr << "EnumItem_GetIndex FAILD" << std::endl;
    }

    return idx;
}

static ani_int returnEnumString([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object) {
    ani_enum enumType;
    if(ANI_OK != env->FindEnum("ani_enum.#COLORSTRING", &enumType)) {
        std::cerr << "Find Enum Faild" << std::endl;
    }
    
    ani_enum_item enumItem;
    if(ANI_OK != env->Enum_GetEnumItemByName(enumType, "GREENSTR", &enumItem)){
        std::cerr << "Enum_GetEnumItemByName FAILD" << std::endl;
    }

    ani_size idx;
    if(ANI_OK != env->EnumItem_GetIndex(enumItem, &idx)){
        std::cerr << "EnumItem_GetIndex FAILD" << std::endl;
    }

    return idx;
}

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    {
    static const char *moduleName = "ani_enum";
    ani_module module;
    if (ANI_OK != env->FindModule(moduleName, &module)) {
        std::cerr << "Not found '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"getBlue", nullptr, reinterpret_cast<void *>(getBlue)},
        ani_native_function {"processEnumInt", nullptr, reinterpret_cast<void *>(processEnumInt)},
        ani_native_function {"processEnumIntReturnNext", nullptr, reinterpret_cast<void *>(processEnumIntReturnNext)},      
    };
    std::cout << "Start bind native methods1 to '" << moduleName << "'" << std::endl;

    if (ANI_OK != env->Module_BindNativeFunctions(module, methods.data(), methods.size())) {
        std::cerr << "Cannot bind native methods1 to '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods1 to '" << moduleName << "'" << std::endl;
    }

    ani_class cls;
    static const char *className = "ani_enum.Utils";
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"processEnumInt", nullptr, reinterpret_cast<void *>(processEnumInt)},
        ani_native_function {"processEnumString", nullptr, reinterpret_cast<void *>(processEnumString)},
        ani_native_function {"processEnumOptional", nullptr, reinterpret_cast<void *>(processEnumOptional)},
        ani_native_function {"returnEnumInt", nullptr, reinterpret_cast<void *>(returnEnumInt)},
        ani_native_function {"returnEnumString", nullptr, reinterpret_cast<void *>(returnEnumString)},
    };
    std::cout << "Start bind native methods2 to '" << className << "'" << std::endl;

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "Cannot bind native methods2 to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods2 to '" << className << "'" << std::endl;


    *result = ANI_VERSION_1;
    return ANI_OK;
}